package edu.mcw.rgd.clinminer.security;

import java.util.Collection;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;

/**
 * Utils class for programmatic access to Spring Security
 * 
 * @author tadamusiak
 * 
 */
@Configurable
@Component
public class Security {
	private static final Logger log = Logger.getLogger(Security.class);

	@Autowired
	@Qualifier("authenticationManager")
	private transient AuthenticationManager authenticationManager;

	public boolean isAuthenticated() {
		if (getAuthentication() == null
				|| !getAuthentication().isAuthenticated()) {
			return false;
		} else {
			return true;
		}
	}

	public void authenticate(String username, String password)
			throws ClinminerSecurityException {
		try {
			// authenticate
			log.debug("Authenticating " + username + "...");
			Authentication request = new UsernamePasswordAuthenticationToken(
					username, password);
			Authentication result = authenticationManager.authenticate(request);
			SecurityContextHolder.getContext().setAuthentication(result);

			// is clinminer user
			if (!isUserinRole(Roles.ROLE_CLINMINER_USER)) {
				// IMPORTANT de-authenticate unauthorized user
				SecurityContextHolder.clearContext();
				log.debug("loginForm() - access denied for " + username);
				throw new ClinminerSecurityException(
						"You are not authorized to access this application");
			}


		} catch (AuthenticationException e) {
			// clear context to remove previous user
			SecurityContextHolder.clearContext();
			log.debug("Authentication failed for " + username + " "
					+ e.getMessage());
			throw new ClinminerSecurityException(e.getMessage());
		}
	}

	public enum Roles {
		ROLE_CLINMINER_USER, ROLE_CLINMINER_CURATOR, ROLE_CLINMINER_ADMIN
	}

	public boolean isUserinRole(Roles role) {
		if (!isAuthenticated()) {
			return false;
		}

		Collection<? extends GrantedAuthority> userAuthorities = getAuthentication()
				.getAuthorities();
		for (GrantedAuthority auth : userAuthorities) {
			if (auth.getAuthority().equals(role.toString())) {
				return true;
			}
		}
		return false;
	}

	public String getHighestRole() {
		if (!isAuthenticated()) {
			return "Anonymous";
		}
		if (isUserinRole(Roles.ROLE_CLINMINER_ADMIN)) {
			return "admin";
		}
		if (isUserinRole(Roles.ROLE_CLINMINER_CURATOR)) {
			return "curator";
		}
		if (isUserinRole(Roles.ROLE_CLINMINER_USER)) {
			return "user";
		}
		// should not happen
		throw new UnsupportedOperationException();
	}

	public String getUsername() {
		if (!isAuthenticated()) {
			return "Anonymous";
		}
		return getAuthentication().getName();
	}

	public void logOut() {
		SecurityContextHolder.clearContext();
	}

	private Authentication getAuthentication() {
		return SecurityContextHolder.getContext().getAuthentication();
	}

}
